﻿#ifndef df_file_h__2014_1_6__10_38
#define df_file_h__2014_1_6__10_38


#include "df_macro.h"

#ifdef _MSC_VER
#	include <shlwapi.h>
#	pragma comment( lib, "shlwapi" )
#endif

namespace df
{
	template<bool _ErrLog = false>
	class BaseFile
	{
#ifdef WIN32
	public:
		enum FilePos {
			Header = FILE_BEGIN,
			Current = FILE_CURRENT,
			End = FILE_END,
		};

		enum {
			filePermissNormal,
			filePermissAll,
		};
	private:
		HANDLE fileHandle_;
#else
	public:
		enum FilePos {
			Header = SEEK_SET,
			Current = SEEK_CUR,
			End = SEEK_END,
		};

		enum {
			filePermissNormal = S_IRUSR | S_IWUSR | S_IXUSR //owner
			| S_IRGRP //S_IWGRP | S_IXGRP	//group
			| S_IROTH, //S_IWOTH | S_IXOTH	//other

			filePermissAll = S_IRUSR | S_IWUSR | S_IXUSR //owner
			| S_IRGRP | S_IWGRP | S_IXGRP	//group
			| S_IROTH | S_IWOTH | S_IXOTH,  //other

		};
	private:

		int fileHandle_;
#endif
	public:
		///每次读或写入文件成功的字节数
		unsigned long succeedByte_;

		BaseFile()
			: fileHandle_(0)
			, succeedByte_(0)
		{
		}

		BaseFile(BaseFile && obj)
			: fileHandle_(obj.fileHandle_)
			, succeedByte_(obj.succeedByte_)
		{
			obj.fileHandle_ = 0;
		}

		~BaseFile()
		{
			Close();
		}

		void Close()
		{
			if (fileHandle_)
			{
#ifdef WIN32
				::CloseHandle(fileHandle_);
#else
				::close(fileHandle_);
#endif
				fileHandle_ = 0;
			}
		}

		bool IsClosed()  const
		{
			return fileHandle_ == 0;
		}

		//*******************************************
		// Summary : 打开文件
		// Parameter - const df::CC & name : 文件名
		// Parameter - bool createFile : 是否创建新文件(传true将自动创建所有子目录)
		// Parameter - bool cover : 是否覆盖/删除旧文件
		// Parameter - bool readOnly : 是否只读
		// Parameter - ErrWrap er : 返回错误信息
		// Returns - bool : 成功与否
		//*******************************************
		bool Open(const df::CC & name
			, bool createFile = true
			, bool cover = false
			, bool readOnly = false
			, df::ErrWrap er = nullptr
			)
		{

			Close();

			if (name.length_ == 0)
			{
				if (er)
					er->Set(df::ErrCode::OpenNullFile, tcc_("文件名不能为空"));
				if (_ErrLog)
				{
					DF_LOG(tcc_("文件名不能为空"));
				}

				return false;
			}

#ifdef WIN32
			DWORD mode = OPEN_EXISTING;
			if (createFile)
				mode = OPEN_ALWAYS;

			if (cover)
				mode = CREATE_ALWAYS;

			fileHandle_ = ::CreateFile(name.char_
				, readOnly ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE
				, FILE_SHARE_READ | FILE_SHARE_WRITE
				, NULL
				, mode
				, FILE_ATTRIBUTE_NORMAL, NULL);

			if (fileHandle_ == INVALID_HANDLE_VALUE)
			{
				if (GetLastError() != 3)
				{
				OPEN_FAILED:
					if (er)
						er->SetLastError(String() << name << tcc_(" 文件打开失败"));
					if (_ErrLog)
					{
						DF_ERR(name << tcc_(" 文件打开失败"));
					}
					fileHandle_ = 0;

					return false;
				}

				if (!createFile && !cover)
					goto OPEN_FAILED;

				//创建文件夹
				if (CreateDirEx(name) == 0)
					goto OPEN_FAILED;

				//再次创建文件
				fileHandle_ = ::CreateFile(name.char_
					, readOnly ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE
					, FILE_SHARE_READ | FILE_SHARE_WRITE
					, NULL
					, mode
					, FILE_ATTRIBUTE_NORMAL, NULL);

				if (fileHandle_ == INVALID_HANDLE_VALUE)
					goto OPEN_FAILED;

			}



#else
			int flags = readOnly ? O_RDONLY : O_RDWR;

			if (createFile)
				flags |= O_CREAT;

			if (cover)
				flags |= O_TRUNC;


			fileHandle_ = ::open(name.char_, flags, filePermissNormal);
			if (fileHandle_ == -1)
			{
				if (df::GetError() != ENOENT)
				{
				OPEN_FAILED:
					if (er)
						er->SetLastError(String() << name << tcc_(" 文件打开失败"));
					if (_ErrLog)
					{
						DF_ERR(name << tcc_(" 文件打开失败"));
					}
					fileHandle_ = 0;
					return false;
				}

				if (!createFile && !cover)
					goto OPEN_FAILED;

				//创建文件夹
				if (CreateDirEx(name) == 0)
					goto OPEN_FAILED;

				//再次创建文件
				fileHandle_ = ::open(name.char_, flags, filePermissNormal);
				if (fileHandle_ == -1)
					goto OPEN_FAILED;
			}



#endif
			return true;
		}

		inline bool OpenReadOnly(const df::CC & name
			, df::ErrWrap er = nullptr
			)
		{
			return Open(name, false, false, true, er);
		}


		inline bool Seek(long long pos, int mode)  const
		{
#ifdef WIN32
			LARGE_INTEGER po = { { 0 } };
			po.QuadPart = pos;
			return ::SetFilePointerEx(fileHandle_, po, &po, (DWORD)mode) == TRUE;
#else
			return ::lseek(fileHandle_, (long)pos, mode) >= 0;
#endif
		}

		//移动文件指针至头部
		inline bool SeekStart(long long pos)  const
		{
			return Seek(pos, Header);
		}

		//从当前文件位置开始移动
		inline bool SeekCurrent(long long pos)  const
		{
			return Seek(pos, Current);
		}
		inline bool SeekEnd(long long pos)  const
		{
			return Seek(pos, End);
		}

		//获取文件大小
		inline long long GetFileSize() const
		{
#ifdef WIN32
			LARGE_INTEGER fSize;
			if (::GetFileSizeEx(fileHandle_, &fSize))
				return fSize.QuadPart;
#else
			struct stat statbuff;
			if (::fstat(fileHandle_, &statbuff) == 0) {
				return statbuff.st_size;
			}
#endif
			return 0;
		}

		//获取读取或写入成功的字节数
		inline unsigned long GetSuccedByte() const
		{
			return succeedByte_;
		}


		bool Read(void * buff, size_t size, ErrWrap er = nullptr)
		{
#ifdef WIN32
			if (!::ReadFile(fileHandle_, buff, (DWORD)size, &succeedByte_, NULL))
			{
				fileHandle_ = 0;
				if (er)
					er->SetLastError(tcc_("文件打开失败"));
				return false;
			}
#else
			succeedByte_ = ::read(fileHandle_, buff, size);
			if ((long)succeedByte_ == -1) {
				fileHandle_ = 0;
				if (er)
					er->SetLastError(tcc_("文件打开失败"));
				return false;
			}
#endif

			return succeedByte_ != 0;
		}

		void ReadToString(String &str, ErrWrap er = nullptr)
		{
			auto fileSize = GetFileSize();
			//100MB
			if (fileSize > 100 * 1024 * 1024)
			{
				if (er)
					er->Set(df::ErrCode::FileTooBig, tcc_("文件过大"));
				if (_ErrLog)
				{
					DF_ERR(tcc_("文件过大:") << fileSize);
				}
				return;
			}
			intptr_t len = (intptr_t)fileSize / sizeof(TCHAR);
			auto old_len = str.size();
			str.resize(len + old_len);
			Read(&str[old_len], len * sizeof(TCHAR), er);
		}

		String ReadToString(ErrWrap er = nullptr)
		{
			String str;
			ReadToString(str, er);
			return str;
		}


		bool Write(const void * buff, size_t size, ErrWrap er = nullptr)
		{
#ifdef WIN32
			if (!::WriteFile(fileHandle_, buff, (DWORD)size, &succeedByte_, NULL))
			{
				fileHandle_ = 0;
				if (er)
					er->SetLastError(tcc_("文件写入失败"));
				return false;
			}
#else
			succeedByte_ = ::write(fileHandle_, buff, size);

			if (((long)succeedByte_) == -1) {
				fileHandle_ = 0;
				if (er)
					er->SetLastError(tcc_("文件写入失败"));
				return false;
			}
#endif

			return (succeedByte_ != 0);
		}


		static CC GetFileName(CC name)
		{
			auto res = name.Find('/', name.length() - 1, false);
			if (res >= 0)
				return CC(name.char_ + res + 1, name.length() - res - 1);

			return name;
		}

		bool Write(const df::CC & cw, ErrWrap er = nullptr)
		{
			return Write(cw.char_, cw.length_ * sizeof(TCHAR), er);
		}


		inline bool SetFileSize(long long size)
		{
#ifdef WIN32
			if (SeekStart(size) && ::SetEndOfFile(fileHandle_))
				return true;
#else
			if (::ftruncate(fileHandle_, size) == 0 && SeekStart(size))
				return true;
#endif
			if (_ErrLog)
			{
				DF_ERR(tcc_("文件长度修改失败"));
			}
			return false;
		}


		static inline bool DelFile(const df::CC & name)
		{
#ifdef WIN32
			return ::DeleteFile(name.char_) == TRUE;
#else
			return ::unlink(name.char_) == 0;
#endif
		}

		//创建一层目录
		static inline bool CreateDir(const df::CC & name)
		{
#ifdef WIN32
			return ::CreateDirectory(name.char_, 0) == TRUE;
#else
			return ::mkdir(name.char_, filePermissNormal) == 0;
#endif
		}

		//CreateDir加强版,支持创建多层子目录,返回创建成功个数
		static int CreateDirEx(const df::CC & name)
		{
			String subDir;
			int succedCount = 0;

			//创建多层子目录
			for (int i = 0; i < name.length_; i++)
			{
				subDir += name[i];
				if (name[i] == _T('\\') || name[i] == _T('/'))
				{
					if (CreateDir(subDir))
						succedCount++;
				}
			}

			return succedCount;
		}


		//删除目录(目录必须为空)
		static inline bool DeleteDir(const df::CC & name)
		{
#ifdef WIN32
			return ::RemoveDirectory(name.char_) == TRUE;
#else
			return ::rmdir(name.char_) == 0;
#endif
		}

		//DeleteDir加强版,包括所有子级
		static bool DeleteDirEx(const CC & menu, ErrWrap er = nullptr)
		{
			String filePath;

			for (auto & file : df::EachDir(menu))
			{
				filePath.clear();
				filePath << menu << tt_('/') << file.name_;
				if (file.IsDir())
				{
					//递归子目录
					if (!DeleteDirEx(filePath, er))
						return false;

					continue;
				}
#ifdef WIN32
				if (file.attributes_
					& (df::FileAttribute::SYSTEM | df::FileAttribute::HIDDEN | df::FileAttribute::READONLY))
				{
					::SetFileAttributes(filePath.c_str(), df::FileAttribute::ARCHIVE);
				}
#endif

				if (!DelFile(filePath))
				{
					if (er)
						er->SetLastError(String() << filePath << tcc_(" 删除失败!"));
					if (_ErrLog)
					{
						DF_ERR(filePath << tcc_(" 删除失败!"));
					}
					return false;
				}
			}

			if (!DeleteDir(menu))
				return false;
			else
				return true;
		}


		//文件是否存在
		static inline bool IsExist(const df::CC & name)
		{
#ifdef WIN32
			return ::PathFileExists(name.char_) == TRUE;
#else
			return ::access(name.char_, F_OK) == 0;
#endif
		}

		static inline bool Rename(const df::CC & nameFrom, const df::CC & nameTo)
		{
			return ::_trename(nameFrom.GetBuffer(), nameTo.GetBuffer()) == 0;
		}

		//************************************
		// Method:    Copy 拷贝文件
		// Returns:   bool
		// Parameter: const df::CC & nameFrom
		// Parameter: const df::CC & nameTo
		// Parameter: bool cover 是否覆盖nameTo
		// Parameter: ErrWrap er
		//************************************
		static bool Copy(const df::CC & nameFrom, const df::CC & nameTo, bool cover = false, ErrWrap er = nullptr)
		{
			BaseFile fromF;
			if (!fromF.OpenReadOnly(nameFrom, er))
				return false;

			BaseFile toF;
			if (!toF.Open(nameTo, true, cover, false, er))
				return false;

			char buf[df::BUFFER_SIZE];
			while (fromF.Read(buf, df::BUFFER_SIZE, er)) {

				toF.Write(buf, fromF.GetSuccedByte(), er);

				if (er && *er) {
					return false;
				}
			}
			

			return true;
		}

		//*******************************************
		// Summary : 写入UNICODE头,little-endian
		// Returns - void :
		//*******************************************
		inline void WriteUnicodeBOM()
		{
			DF_ASSERT(fileHandle_);

			if (GetFileSize() == 0)
			{
				Write("\xFF\xFE", 2);
			}
		}

		//*******************************************
		// Summary : 写入UTF8头,little-endian
		// Returns - void :
		//*******************************************
		inline void WriteUTF8BOM()
		{
			DF_ASSERT(fileHandle_);

			if (GetFileSize() == 0)
			{
				Write("\xEF\xBB\xBF", 3);
			}
		}

		//检测bom，返回bom长度 0：无bom,2：unicode，3:UTF8
		static uint8_t CheckBOM(const CC & str)
		{
			intptr_t len = str.length_ * sizeof(TCHAR);

			if (len < 2)
				return 0;
			union Bom
			{
				uint32_t val;
				char raw[4];
			};
			Bom Uh;
			Uh.val = 0;

			char * strbuf = (char*)str.char_;

			Uh.raw[0] = strbuf[0];
			Uh.raw[1] = strbuf[1];
			if (Uh.val == 0xFEFF)
			{
				return 2;
			}

			if (len < 3)
				return 0;

			Uh.raw[2] = strbuf[2];
			if (Uh.val == 0xBFBBEF)
			{
				return 3;
			}

			return 0;
		}


		DF_DISABLE_COPY_ASSIGN(BaseFile);
	};


	using File = BaseFile<true>;
	using FilePtr = std::shared_ptr<File>;

	using FileNoLog = BaseFile<false>;


}

#endif // df_file_h__2014_1_6__10_38
